#include "lua_json.h"

static luaL_Reg lib_functions[] =
{
    { "decode", &lua_json::decode},
    { "encode", &lua_json::encode},
    { NULL, NULL}
};

void lua_json::initialize(lua_State* L, const char* lib_name)
{
    lua_newtable(L);

    luaL_Reg* fun_info = lib_functions;
    while (fun_info->func)
    {
        lua_pushcfunction(L, fun_info->func);
        lua_setfield(L, -2, fun_info->name);
        fun_info++;
    }
    lua_setfield(L, -2, lib_name);
}

void lua_json::uninitialize()
{

}

int lua_json::decode(lua_State* L)
{
    int nTop = lua_gettop(L);
    std::string strJson;
    if (nTop >= 1 && lua_isstring(L, 1))
    {
        strJson = lua_tostring(L, 1);
    }

    fcjson::json_value value;
    value.parse(strJson);

    _decode_value(L, value);

    return 1;
}

int lua_json::encode(lua_State* L)
{
    std::string strJson;
    _encode_object(L, strJson);
    lua_pushstring(L, strJson.c_str());
    return 1;
}

void lua_json::_decode_object(lua_State* L, const fcjson::json_value& val)
{
    lua_newtable(L);
    if (val.count() > 0)
    {
        fcjson::json_object& object = val.as_object();
        for (const auto& item : object)
        {
            _decode_value(L, item.second);
            lua_setfield(L, -2, item.first.c_str());
        }
    }
    else
    {
        lua_pushfstring(L, "null");
        lua_setfield(L, -2, "null");
    }
}

void lua_json::_decode_array(lua_State* L, const fcjson::json_value& val)
{
    lua_newtable(L);
    int index = 1;
    if (val.count() > 0)
    {
        fcjson::json_array& array = val.as_array();
        for (const auto& item : array)
        {
            _decode_value(L, item);
            lua_rawseti(L, -2, index++);
        }
    }
    else
    {
        lua_pushfstring(L, "null");
        lua_rawseti(L, -2, index++);
    }
}

void lua_json::_decode_value(lua_State* L, const fcjson::json_value& val)
{
    if (val.is_null())
    {
        lua_pushnumber(L, 0);
    }
    else if (val.is_bool())
    {
        lua_pushboolean(L, val.as_bool());
    }
    else if (val.is_int())
    {
        lua_pushinteger(L, val.as_int());
    }
    else if (val.is_float())
    {
        lua_pushnumber(L, val.as_float());
    }
    else if (val.is_string())
    {
        std::string strText = val.as_string();
        lua_pushstring(L, strText.c_str());
    }
    else if (val.is_array())
    {
        _decode_array(L, val);
    }
    else if (val.is_object())
    {
        _decode_object(L, val);
    }
}

void lua_json::_encode_object(lua_State* L, std::string& strJson)
{
    int count = 0;
    bool flag_array = true;
    lua_pushnil(L);

    while (0 != lua_next(L, -2))
    {
        if (LUA_TNUMBER != lua_type(L, -2))
        {
            flag_array = false;
        }

        count++;
        lua_pop(L, 1);
    }

    lua_pushnil(L);

    if (flag_array)
    {
        strJson += "[";
        while (0 != lua_next(L, -2))
        {
            _encode_value(L, -1, strJson);
            count--;

            if (count > 0)
            {
                strJson += ",";
            }

            lua_pop(L, 1);
        }
        strJson += "]";
    }
    else
    {
        strJson += "{";
        while (0 != lua_next(L, -2))
        {
            _encode_value(L, -2, strJson);
            strJson += ":";
            _encode_value(L, -1, strJson);
            count--;

            if (count > 0)
            {
                strJson += ",";
            }

            lua_pop(L, 1);
        }
        strJson += "}";
    }
}

void lua_json::_encode_value(lua_State* L, int index, std::string& strJson)
{
    switch (lua_type(L, index))
    {
    case LUA_TNIL:
        strJson += "nil";
        break;
    case LUA_TBOOLEAN:
        strJson += lua_toboolean(L, index) ? "true" : "false";
        break;
    case LUA_TNUMBER:
        {
        char buffer[256] = { 0 };
        sprintf_s(buffer, "%g", lua_tonumber(L, index));
        strJson += buffer;
    }
        break;
    case LUA_TSTRING:
        strJson += "\"";
        strJson +=  lua_tostring(L, index);
        strJson += "\"";
        break;
    case LUA_TTABLE:
        _encode_object(L,strJson);
        break;
    default:
        break;
    }
}
